package im.oen.boot.common.utils;

import im.oen.boot.common.constant.Charsets;

import java.util.Random;

/**
 * <p>描述：TOTP算法工具</p>
 *
 * <p>创建时间：2021-12-06 17:42</p>
 * <p>更新时间：暂无</p>
 *
 * @author Kevin.Xu
 * @version 1.0
 */
public class TOTP {

    public static String generateKey() {
        byte[] key = new byte[20];
        new Random().nextBytes(key);
        return new String(CodingTool.b32Encode(key), Charsets.UTF_8);
    }

    public static String generateUrl() {
        return generateUrl(null, null);
    }

    public static String generateUrl(String account, String mark) {
        if (Checker.isEmpty(account)) {
            account = "oen";
        }
        if (Checker.isEmpty(mark)) {
            mark = "oen-test";
        }
        return String.format("otpauth://totp/%s?secret=%s&issuer=%s&algorithm=SHA1&digits=6&period=30", account, generateKey(), mark);
    }

    public static String generate(String key) {
        return generate(key, 30, 6);
    }

    public static String generate(String key, int step) {
        return generate(key, step, 6);
    }

    /**
     * @param secret 用户密钥
     * @param period 步进时长 (秒：s)
     * @param digits 验证码长度 (min: 6)
     * @return totp 验证码
     */
    public static String generate(String secret, int period, int digits) {
        Checker.asserts(Checker.isNotEmpty(secret));
        byte[] k = CodingTool.b32Decode(secret.toUpperCase().getBytes(Charsets.UTF_8));
        byte[] t = ByteUtil.long2bytes(DateUtil.currentTimeSecond() / period);
        byte[] hash = Digest.HMAC_SHA1(t, k);
        int offset = hash[hash.length - 1] & 0xf;
        int binary = ((hash[offset] & 0x7f) << 24) |
                ((hash[offset + 1] & 0xff) << 16) |
                ((hash[offset + 2] & 0xff) << 8) |
                (hash[offset + 3] & 0xff);
        int otp = binary % 1000000;
        String result = Integer.toString(otp);
        while (result.length() < digits) {
            result = "0" + result;
        }
        return result;
    }
}
